package cn.bearspark.framework.web.starter.interceptor;

import cn.bearspark.framework.web.starter.context.BSRequestContext;
import cn.bearspark.framework.web.starter.exception.RequestLimitException;
import cn.bearspark.framework.web.starter.limit.RequestLimit;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 对请求进行限流的拦截器
 */
@Component
public class RequestLimitInterceptor implements HandlerInterceptor {

    private final RedisTemplate<String, Object> redisTemplate;

    public RequestLimitInterceptor(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Value("${spring.application.name}")
    private String applicationName;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return true;
        }

        // 如果方法没有被 @RequestLimit 注解，直接放过
        if (!handlerMethod.getMethod().isAnnotationPresent(RequestLimit.class)) {
            return true;
        }

        long userId = BSRequestContext.getUserId();
        String cacheKey = applicationName + ":" + userId + ":" + request.getRequestURI();
        RequestLimit requestLimit = handlerMethod.getMethod().getAnnotation(RequestLimit.class);
        int limit = requestLimit.limit();
        int second = requestLimit.second();

        // 如果是第一次请求，直接放过
        int reqTimes = (int) Optional.ofNullable(redisTemplate.opsForValue().get(cacheKey)).orElse(0);
        if (reqTimes == 0) {
            redisTemplate.opsForValue().set(cacheKey, 1, second, TimeUnit.SECONDS);
            return true;
        }

        // 如果请求次数没有超过限制，直接放过
        if (reqTimes < limit) {
            redisTemplate.opsForValue().increment(cacheKey, 1);
            return true;
        }

        // 如果请求次数超过限制，抛出异常
        throw new RequestLimitException(requestLimit.msg());
    }

}
